#pragma once
#include <map>
#include <utility>

namespace rh
{
	template <typename ReturnT, typename ParamT>
	class HandlerBase
	{
	public:
		virtual ReturnT Process(ParamT message) = 0;
	};


	template <typename TargetT, typename ReturnT, typename ParamT>
	class Handler : public HandlerBase<ReturnT,ParamT>
	{
	private:
		typedef ReturnT (TargetT::*method_t)(ParamT);
		TargetT* mObject;
		method_t mMethod;

	public:
		Handler(TargetT* object, method_t method)
			: mObject(object), mMethod(method) { }

		ReturnT Process(ParamT param) override
		{
			return (mObject->*mMethod)(param);
		}
	};


	template <typename ReturnT,typename ParamT>
	class Event
	{
	private:
		typedef std::multimap<long, HandlerBase<ReturnT,ParamT>*> eventMap;

		long id;
		static long counter;
		static eventMap mapper;
		typename static eventMap::iterator iter;

		static eventMap StartMapping()
		{
			eventMap temp;
			return temp;
		}

	public:
		Event() : id(++counter) { };

		~Event()
		{
			for(iter = mapper.find(id); iter != mapper.end(); iter = mapper.find(id))
			{
				delete iter->second;
				mapper.erase(iter);
			}
		}

		template<typename TargetT>
		void attach(TargetT* object, ReturnT (TargetT::*method)(ParamT))
		{
			mapper.insert(std::make_pair(id, new Handler<TargetT, ReturnT, ParamT>(object, method)));
		}

		void Emit(ParamT param)
		{
			for(iter = mapper.begin(); iter != mapper.end(); ++iter)
				if (iter->first == id)
					iter->second->Process(param);
		}
	};
	template<typename ReturnT,typename ParamT> 
	long Event<typename ReturnT,typename ParamT>::counter = 0;

	template<typename ReturnT,typename ParamT>
	typename Event<typename ReturnT,typename ParamT>::eventMap::iterator
		Event<typename ReturnT,typename ParamT>::iter = Event<typename ReturnT,typename ParamT>::mapper.begin();

	template<typename ReturnT,typename ParamT> 
	typename Event<typename ReturnT,typename ParamT>::eventMap
		Event<typename ReturnT,typename ParamT>::mapper(Event<typename ReturnT,typename ParamT>::StartMapping());


	template<typename ReturnT,typename ParamT>
	class Object
	{
	protected:
		virtual ~Object() = 0 { }

	public:
		template<typename TargetT>
		void Register(Event<ReturnT, ParamT>& evn, ReturnT (TargetT::*method)(ParamT))
		{
			evn.attach(dynamic_cast<TargetT*>(this), method);
		}
	};
}